package com.movie.ticket.sale.authorassembly.component;

import com.movie.ticket.sale.moviecommon.common.Result;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author： Micsal
 * @create-time： 2020.12.13 20:15
 * @version： 1.4
 * @description： ftp上传文件辅助类, Result.data 是 String[]数组，与文件一一对应的url。
 * 一致性：多文件上传，一个失败，已上传的全部删除。
 */
@Slf4j
public class UpdateUtil {

    /**
     * 父级文件目录
     */
    private final String DIRNAME = "cinema comment movie person sundry user";

    /**
     * ftp服务客户端
     */
    private FTPClient ftpClient = new FTPClient();

    /**
     * 本地字符编码集
     */
    private String localCharset = "GBK";


    /**
     * 根据url下载文件
     * @param url   传入格式：http://81.69.245.134/sundry/default-contract.jpg
     * @return 返回的是文件流 InputStream
     */
    public Result downloadFile(String url) {
        try {
            if(!url.startsWith("http://81.69.245.134"))
                return Result.fail(426,"下载地址不合法");
            String s1 = "http://81.69.245.134/sundry/default-contract.jpg";
            s1=url;
            s1 = s1.substring(21);
            String s2[] = s1.split("/");
            int len = s2.length;
            String doc = "";
            String name = s2[len - 1];
            for (int i = 0; i < len - 1; i++)
                doc += s2[i] + "/";

            System.out.println(doc.length());
            doc=doc.substring(0, doc.length() - 1);

            Result cr = connect();
            if (!cr.isSuccess())
                return cr;

            setRule();

            ftpClient.changeWorkingDirectory(doc);
            InputStream inputStream1 = ftpClient.retrieveFileStream(name);
            System.out.println(inputStream1==null);

            return Result.success(inputStream1);
        } catch (Exception e) {
            e.printStackTrace();
            return Result.fail(426, e.getMessage());
        }
    }

    /**
     * 单个InputStream上传
     * @param father        固定的父级目录：cinema comment movie person sundry user
     * @param inputStream   文件流
     * @param id            操作对象数据库表的id
     * @param name          带后缀文件名
     * @return
     */
    public Result updateOneInputStream(String father, InputStream inputStream, int id,String name) {
        if (!DIRNAME.contains(father))
            return Result.fail(426, "非法的父级目录名");
        if (null == inputStream)
            return Result.fail(426, "非法文件流");
        if (id <= 0)
            return Result.fail(426, "非法id");

        Result cr = connect();
        if (!cr.isSuccess())
            return cr;

        String directory = father + "_" + id;
        Result dr = cdDir(father, directory);
        if (!dr.isSuccess())
            return dr;

        setRule();

        String url = "";
        try {
            String fileName = UUID.randomUUID().toString() + reCoding(name);
            if (ftpClient.storeFile(fileName, inputStream)) {
                log.info("------ 文件上传成功：{}", fileName);
                url = "http://81.69.245.134/" + father + "/" + directory + "/" + fileName;
                log.info("------ 地址为：{}", url);
            } else {
                log.info("------ 文件上传失败：{}", fileName);
                return Result.fail(426, "文件上传失败");
            }
            inputStream.close();
            ftpClient.logout();
        } catch (IOException e) {
            String message = e.getMessage();
            log.info("------ ftp上传文件IO异常:{}", message);
            e.printStackTrace();
            return Result.fail(426, "ftp上传文件IO异常" + message);
        }
        return Result.success(url);
    }

    /**
     * 单个文件上传
     * @param father 固定的父级目录：cinema comment movie person sundry user
     * @param file   文件
     * @param id     操作对象数据库表的id
     * @return
     */
    public Result updateOneFile(String father, MultipartFile file, int id) {
        if (!DIRNAME.contains(father))
            return Result.fail(426, "非法的父级目录名");
        if (null == file)
            return Result.fail(426, "非法文件空值");
        if (id <= 0)
            return Result.fail(426, "非法id");

        Result cr = connect();
        if (!cr.isSuccess())
            return cr;

        String directory = father + "_" + id;
        Result dr = cdDir(father, directory);
        if (!dr.isSuccess())
            return dr;

        setRule();

        String url = "";
        try {
            String fileName = UUID.randomUUID().toString() + reCoding(file.getOriginalFilename());
            InputStream inputStream = file.getInputStream();
            if (ftpClient.storeFile(fileName, inputStream)) {
                log.info("------ 文件上传成功：{}", fileName);
                url = "http://81.69.245.134/" + father + "/" + directory + "/" + fileName;
                log.info("------ 地址为：{}", url);
            } else {
                log.info("------ 文件上传失败：{}", fileName);
                return Result.fail(426, "文件上传失败");
            }
            inputStream.close();
            ftpClient.logout();
        } catch (IOException e) {
            String message = e.getMessage();
            log.info("------ ftp上传文件IO异常:{}", message);
            e.printStackTrace();
            return Result.fail(426, "ftp上传文件IO异常" + message);
        }
        return Result.success(url);
    }

    /**
     * 多文件上传
     *
     * @param father 父级目录
     * @param files  文件数组
     * @param id     对应id
     * @return
     */
    public Result updateFiles(String father, MultipartFile[] files, int id) {
        if (!DIRNAME.contains(father))
            return Result.fail(426, "非法的父级目录名");
        if (null == files || files.length == 0)
            return Result.fail(426, "非法文件数组");
        if (id <= 0)
            return Result.fail(426, "非法id");

        Result cr = connect();
        if (!cr.isSuccess())
            return cr;

        String directory = father + "_" + id;
        Result dr = cdDir(father, directory);
        if (!dr.isSuccess())
            return dr;

        setRule();

        String[] urls = new String[files.length];
        String[] names = new String[files.length];
        int i = 0;
        //上传文件
        try {
            for (MultipartFile f : files) {
                if (null != f) {//判断非空
                    String fileName = UUID.randomUUID().toString() + reCoding(f.getOriginalFilename());
                    InputStream inputStream = f.getInputStream();
                    if (ftpClient.storeFile(fileName, inputStream)) {
                        log.info("------ 文件上传成功：{}", fileName);
                        urls[i] = "http://81.69.245.134/" + father + "/" + directory + "/" + fileName;
                        log.info("------ 地址为：{}", urls[i]);
                        i++;
                    } else {
                        log.info("------ 文件上传失败：{}，开始清空已上传文件", fileName);
                        for (String name : names) {
                            boolean delIt = false;
                            int j = 0;
                            do {
                                delIt = ftpClient.deleteFile(name);
                                log.info("------ 删除文件：{}，尝试第{}次", fileName, j);
                                j++;
                                if (j == 10) {
                                    j = 0;
                                    continue;
                                }
                            } while (!delIt);
                        }
                        return Result.fail(426, "文件上传失败");
                    }
                    inputStream.close();
                }
            }
            ftpClient.logout();
        } catch (IOException e) {
            String message = e.getMessage();
            log.info("------ ftp上传文件IO异常:{}", message);
            e.printStackTrace();
            return Result.fail(426, "ftp上传文件IO异常" + message);
        }
        return Result.success(urls);
    }

    /**
     * 文件名重编码
     *
     * @param old
     * @return
     */
    private String reCoding(String old) {
//        System.out.println(old);
        Pattern pat = Pattern.compile("[\u4e00-\u9fa5]");
        Matcher mat = pat.matcher(old);
        old = mat.replaceAll("");
        String tmp = "";
        char c;
        for (int i = 0; i < old.length(); i++) {
            c = old.charAt(i);
//            if (c != ':' && c != '.' && c != '/' && c != '\\' && c != '?' && c != '=' &&
//                    c != '&' && c != '@' && c != '!' && c != '#' && c != '$' && c != '%' &&
//                    c != '^' && c != '(' && c != ')' && c != '"' && c != '<' && c != '>' &&
//                    c != ',' && c != ' ' && c != '[' && c != ']' && c != '{' && c != '}' &&
//                    c != '_' && c != '-' && c != '+' && c != ';' && c != '`' && c != '~' &&
//                    c != '|')
            if ((c >= 45 && c <= 57) || (c >= 97 && c <= 122) || (c >= 65 && c <= 90))
                tmp += c;
        }

        try {
            tmp = new String(tmp.getBytes(localCharset), StandardCharsets.ISO_8859_1);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            log.info("重编码异常：{}", e.getMessage());
        }
//        System.out.println(tmp);
        return tmp;
    }


    /**
     * 设置传输规则
     */
    private void setRule() {
        //设置传输规则
        try {
            ftpClient.setControlEncoding("UTF-8");
            ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
            ftpClient.setFileTransferMode(FTPClient.BINARY_FILE_TYPE);
            if (FTPReply.isPositiveCompletion(ftpClient.sendCommand("OPTS UTF8", "ON")))
                localCharset = "UTF-8";
            ftpClient.enterLocalPassiveMode();
        } catch (IOException e) {
            log.info("------ ftp设置异常:{}", e.getMessage());
            e.printStackTrace();
        }

    }


    /**
     * 进入工作目录：已存在-进入，不存在-创建再进入
     */
    private Result cdDir(String father, String directory) {
        try {
            int code = ftpClient.cwd(father + "/" + directory);
            if (code == 250) {
                log.info("------ 工作目录切换成功");
                return Result.success();
            } else {
                log.info("------ 工作目录切换失败，创建工作目录");
                //进入父目录
                if (!ftpClient.changeWorkingDirectory(father)) {
                    log.info("------ 切换父目录 {} 失败", father);
                    return Result.fail(426, "切换 " + father + " 失败");
                }
                //创建子目录
                if (!ftpClient.makeDirectory(directory)) {
                    log.info("------ 创建子目录 {} 失败", directory);
                    return Result.fail(426, "创建目录 " + directory + " 失败");
                } else {
                    log.info("------ 创建子目录 {} 成功", directory);
                    //切换子目录
                    if (!ftpClient.changeWorkingDirectory(directory)) {
                        log.info("------ 切换子目录 {} 失败", directory);
                        return Result.fail(426, "切换 " + directory + " 失败");
                    } else {
                        log.info("------ 切换子目录 {} 成功", directory);
                        return Result.success();
                    }
                }
            }
        } catch (IOException e) {
            String message = e.getMessage();
            log.info("------ cwd工作目录失败：{}", message);
            e.printStackTrace();
            return Result.fail(426, message);
        }

    }

    /**
     * 连接,登录
     */
    private Result connect() {
        try {
            //连接
            ftpClient.connect("81.69.245.134", 56230);

            //登录
            boolean isFTPLogin = ftpClient.login("ftpadmin", "yE532a");
            if (!isFTPLogin) {
                log.info("------ FTP登录失败");
                String message = ftpClient.getReplyString();
                return Result.fail(426, "FTP登录失败:" + message);
            } else
                log.info("------ FTP登录成功");

        } catch (IOException e) {
            log.info(e.getMessage());
            e.printStackTrace();
            return Result.fail(426, e.getMessage());
        }
        return Result.success();
    }

}
